package joe;

import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;

final class MemPool {
	private static final int PAGE_SIZE = 0x10000;

	private final ArrayList<Long> allocPtrs = new ArrayList<>();
	private long freeList16Ptr;
	private long freeList64Ptr;

	MemPool() {
		var allocPtrs0 = allocPtrs;
		Cleaner.addGlobal(this, () -> {
			for (int i = allocPtrs0.size(); --i >= 0; )
				UnsafeUtil.freeMemory(allocPtrs0.get(i));
		});
	}

	static long align(long ptr, long align) {
		--align;
		return (ptr + align) & ~align;
	}

	private long allocPage(long unitSize) {
		var p = UnsafeUtil.allocateMemory(PAGE_SIZE);
		allocPtrs.add(p);
		var e = p + PAGE_SIZE - unitSize * 2;
		p = align(p, unitSize);
		for (long i = p, j; ; i = j) {
			j = i + unitSize;
			UnsafeUtil.putLong(i, j);
			if (j > e) {
				UnsafeUtil.putLong(j, 0);
				return p;
			}
		}
	}

	long alloc16(@NotNull Object obj) {
		var freeListPtr = freeList16Ptr;
		if (freeListPtr == 0)
			freeListPtr = allocPage(16);
		freeList16Ptr = UnsafeUtil.getLong(freeListPtr);
		var freeListPtr0 = freeListPtr;
		new Cleaner.Node(obj, Engine.current().getCleaner()) {
			@Override
			protected void clean() {
				UnsafeUtil.putLong(freeListPtr0, freeList16Ptr);
				freeList16Ptr = freeListPtr0;
			}
		};
		return freeListPtr;
	}

	long alloc64(@NotNull Object obj) {
		var freeListPtr = freeList64Ptr;
		if (freeListPtr == 0)
			freeListPtr = allocPage(64);
		freeList64Ptr = UnsafeUtil.getLong(freeListPtr);
		var freeListPtr0 = freeListPtr;
		new Cleaner.Node(obj, Engine.current().getCleaner()) {
			@Override
			protected void clean() {
				UnsafeUtil.putLong(freeListPtr0, freeList64Ptr);
				freeList64Ptr = freeListPtr0;
			}
		};
		return freeListPtr;
	}
}
